feat: server-generated PDF export for task and patient tables#232
Merged
Conversation
Adds a Print button to the task and patient tables that produces a clean, paginated PDF of the current view via a new backend endpoint, so a ward list can be printed or kept as an offline paper copy. Backend: - New POST /export/table.pdf endpoint (routers/export.py) that renders an autoescaped Jinja2 HTML table (A4, repeating header, page numbers, generated timestamp, helpwave branding) to PDF with WeasyPrint. WeasyPrint is imported lazily and returns 503 if the native libraries are unavailable, so the app still boots without them. The endpoint requires an authenticated user. - Row/column normalization (pad/truncate to column count, caps) and a safe download filename. - Add weasyprint + jinja2 to requirements.txt and the Pango/fontconfig/font runtime libraries to the backend Dockerfile. - Unit tests for HTML rendering, escaping, normalization and filename safety. Frontend: - TableExportButton reads the visible columns and loaded rows straight from the table instance and posts them to the endpoint, opening the returned PDF. - Wire the button into the TaskList and PatientList toolbars; add a `print` translation key across all locales.
…3018) Pin the Pango/fontconfig/font runtime libraries to their Alpine 3.22 versions (matching the base image) and use the current font-dejavu package name instead of the renamed ttf-dejavu.
…anch The earlier pin used Alpine v3.22 versions, but the base image tracks a newer branch (gcc 15.2.0-r2), so apk could not satisfy the constraints and the image build failed. Leave the Pango/fontconfig/font packages unpinned with an explicit hadolint ignore (DL3018) so they resolve against the base image's own Alpine branch at build time.
# Conflicts: # web/components/tables/PatientList.tsx # web/components/tables/TaskList.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the sketchy CSS-only printing with a real Print button that produces a clean, paginated PDF of the task/patient table via a new backend endpoint (FastAPI + WeasyPrint) — usable as an offline paper copy.
Backend
POST /export/table.pdf(backend/routers/export.py): renders an autoescaped Jinja2 HTML table to PDF with WeasyPrint. The template is A4 with a repeating header row, page numbers, a generated timestamp, helpwave branding and zebra striping; orientation defaults to landscape.503if the native libraries are missing, so the app still boots in environments without them. The endpoint requires an authenticated user.requirements.txt: addweasyprint==63.1andjinja2==3.1.4.Dockerfile: add the Pango/fontconfig/font runtime libraries WeasyPrint needs (unpinned — see caveat).Frontend
TableExportButtonreads the currently visible columns and loaded rows directly from the table instance (useTableStateContext) and posts them to the endpoint, then opens the returned PDF (falls back to download if pop-ups are blocked). Because the client sends the rendered cells, the PDF matches exactly what the user sees and reuses the GraphQL query's existing auth/filters.TaskListandPatientListtoolbars; added aprinttranslation key across all six locales.Validation
pytest tests/unit/test_export_pdf.py✅ (7 tests). In this environment WeasyPrint rendered a real PDF end-to-end (8 KB,%PDF-header). The 3 unrelatedtest_base_resolvers.pyfailures are pre-existing on a clean tree (environmental).npm run lint(tsc + eslint) ✅,npm run build✅,npm run check-translations✅.Caveats / follow-ups
pango fontconfig ttf-dejavu); the repo pins all apk versions, so these should be pinned to the target Alpine digest's versions. I couldn't build the image here to determine them.Generated by Claude Code